package com.liziguo.lzgfp.server.netty;

import com.liziguo.lzgfp.server.netty.handler.RouterHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;

/**
 * @Author: Liziguo
 * @Date: 2021/12/24 17:13
 */
@Component
public class Server implements AutoCloseable {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private final int port;
    public ChannelFuture channelFuture;
    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;

    public Server(int port) {
        this.port = port;
    }

    public static void main(String[] args) throws InterruptedException {
        new Server(9001).run();
    }

    public void run() throws InterruptedException {
        bossGroup = new NioEventLoopGroup(1); //用于处理服务器端接收客户端连接
        workerGroup = new NioEventLoopGroup(); //进行网络通信（读写）
        final ServerBootstrap bootstrap = new ServerBootstrap(); //辅助工具类，用于服务器通道的一系列配置
        bootstrap.group(bossGroup, workerGroup) //绑定两个线程组
                .option(ChannelOption.SO_BACKLOG, 128) //初始化服务器可连接队列大小 设置TCP缓冲区
//                .option(ChannelOption.SO_SNDBUF, 32 * 1024) //设置发送数据缓冲大小
//                .option(ChannelOption.SO_RCVBUF, 32 * 1024) //设置接受数据缓冲大小
                .childOption(ChannelOption.SO_KEEPALIVE, true) //保持连接活跃状态
                .channel(NioServerSocketChannel.class) //指定NIO的模式
                .childHandler(new ChannelInitializer<SocketChannel>() { //配置具体的数据处理方式
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        final ChannelPipeline pipeline = socketChannel.pipeline();
                        /**
                         * lengthFieldLength:长度字段长度
                         * lengthIncludesLengthFieldLength：长度是否 包括长度字段长度
                         */
                        pipeline.addLast(new LengthFieldPrepender(3));
                        /**
                         * maxFrameLength：指定了每个包所能传递的最大数据包大小；
                         * lengthFieldOffset：指定了长度字段在字节码中的偏移量；
                         * lengthFieldLength：指定了长度字段所占用的字节长度；
                         * lengthAdjustment：对一些不仅包含有消息头和消息体的数据进行消息头的长度的调整，这样就可以只得到消息体的数据，这里的lengthAdjustment指定的就是消息头的长度；
                         * initialBytesToStrip：对于长度字段在消息头中间的情况，可以通过initialBytesToStrip忽略掉消息头以及长度字段占用的字节。
                         */
                        pipeline.addLast(new LengthFieldBasedFrameDecoder(1024 * 1024, 0, 3, 0, 3));
                        pipeline.addLast(new StringEncoder(StandardCharsets.UTF_8));
                        pipeline.addLast(new StringDecoder(StandardCharsets.UTF_8));
                        pipeline.addLast(new RouterHandler());
                    }
                });
        channelFuture = bootstrap.bind(port).sync();
        logger.info("服务器开启成功 客户端连接端口:{}", port);
    }

    @Override
    public void close() throws Exception {
        logger.info("服务器关闭 port:{}", port);
        channelFuture.channel().close();
        if (bossGroup != null) bossGroup.shutdownGracefully();
        if (workerGroup != null) workerGroup.shutdownGracefully();
    }
}
